/**
 * 
 */
package org.metaverse.warehouse.util;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.metaverse.warehouse.Senary;
import org.metaverse.warehouse.rdf.Converter;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;

/**
 * @author vjache
 *
 */
@SuppressWarnings("serial")
public abstract class AbstractSenary implements Senary {

	private final Resource _subject;
	private final URI _predicate;
	private final Value _object;
	private final Resource _context;
	private final URI _user;
	private final Date _lastAssertTime;

	protected AbstractSenary(
			Resource subject, 
			URI predicate, 
			Value object,
			Resource context, 
			URI user, 
			Date lastAssertTime) {
		super();
		_subject = subject;
		_predicate = predicate;
		_object = object;
		_context = context;
		_user = user;
		_lastAssertTime = lastAssertTime;
	}
	protected AbstractSenary(
			Statement aStmtToCopy, 
			URI user, 
			Date lastAssertTime) {
		this(	aStmtToCopy.getSubject(),
				aStmtToCopy.getPredicate(),
				aStmtToCopy.getObject(),
				aStmtToCopy.getContext(),
				user,
				lastAssertTime);
	}
	protected AbstractSenary(
			Statement aStmtToCopy) {
		this(	aStmtToCopy.getSubject(),
				aStmtToCopy.getPredicate(),
				aStmtToCopy.getObject(),
				aStmtToCopy.getContext(),
				null,
				null);
	}
	
	protected abstract ValueFactory getValueFactory();
	
	/* (non-Javadoc)
	 * @see org.metaverse.warehouse.Senary#getLastAssertTime()
	 */
	@Override
	public Date getLastAssertTime() 
	{return _lastAssertTime;}

	/* (non-Javadoc)
	 * @see org.metaverse.warehouse.Senary#getSlots(org.metaverse.warehouse.Senary.Slot[])
	 */
	@Override
	public List<Value> getSlots(Slot... aSlot) {
		ArrayList<Value> list = new ArrayList<Value>(aSlot.length);
		for(Slot slot : aSlot)
		{
			list.add(slot(slot));
		}
		return list;
	}

	protected final Value slot(Slot slot) {
		final Value v;
		switch(slot)
		{
		case Subject: v=_subject;break;
		case Predicate: v=_predicate;break;
		case Object: v=_object;break;
		case Context: v=_context;break;
		case User: v=_user;break;
		case LastAssertTime: v=Converter.valueOf(_lastAssertTime, getValueFactory());break;
		default:
			throw new IllegalArgumentException(String.valueOf(slot));
		}
		return v;
	}

	/* (non-Javadoc)
	 * @see org.metaverse.warehouse.Senary#getUser()
	 */
	@Override
	public URI getUser() 
	{return _user;}

	/* (non-Javadoc)
	 * @see org.openrdf.model.Statement#getContext()
	 */
	@Override
	public Resource getContext() 
	{return _context;}

	/* (non-Javadoc)
	 * @see org.openrdf.model.Statement#getObject()
	 */
	@Override
	public Value getObject() 
	{return _object;}

	/* (non-Javadoc)
	 * @see org.openrdf.model.Statement#getPredicate()
	 */
	@Override
	public URI getPredicate() 
	{return _predicate;}

	/* (non-Javadoc)
	 * @see org.openrdf.model.Statement#getSubject()
	 */
	@Override
	public Resource getSubject() 
	{return _subject;}

	/* (non-Javadoc)
	 * @see java.lang.Iterable#iterator()
	 */
	@Override
	public Iterator<Value> iterator() {
		final Slot[] values = Slot.values();
		return new Iterator<Value>() {
			private int i=0;
			
			@Override
			public void remove() 
			{throw new UnsupportedOperationException();}
			
			@Override
			public Value next() {
				return slot(values[i++]);
			}
			
			@Override
			public boolean hasNext() 
			{return i<values.length;}
		};
	}
	
	@Override
	public String toString() 
	{return getSlots(Slot.values()).toString();}
	
	@Override
	public boolean equals(Object obj) {
		if(this == obj)
			return true;
		if(!(obj instanceof Senary))
			return false;
		Senary s = (Senary)obj;
		return 
			equals(_subject,s.getSubject()) &&
			equals(_predicate,s.getPredicate()) && 
			equals(_object,s.getObject()) && 
			equals(_context,s.getContext()) && 
			equals(_user,s.getUser()) && 
			equals(_lastAssertTime,s.getLastAssertTime());
	}
	
	private final boolean equals(Object o1, Object o2)
	{
		if(o1 == o2)
			return true;
		if(o1 != null)
			return o1.equals(o2);
		else
			return false;
	}
	
	@Override
	public int hashCode() {
		return 
			 hashCodeOf(_subject)
			^hashCodeOf(_predicate)
			^hashCodeOf(_object)
			^hashCodeOf(_context)
			^hashCodeOf(_user)
			^hashCodeOf(_lastAssertTime);
	}
	
	private final int hashCodeOf(Object aObj)
	{return aObj == null?0:aObj.hashCode();}

}
